From 53ba6cbd44fd22ce1c659a6de09d41b84ab2530a Mon Sep 17 00:00:00 2001 From: as51340 Date: Tue, 4 Aug 2026 12:39:38 +0200 Subject: [PATCH 1/4] feat: SSO coords --- pages/clustering/high-availability.mdx | 5 + pages/clustering/high-availability/_meta.ts | 1 + .../coordinator-authentication.mdx | 592 ++++++++++++++++++ .../ha-commands-reference.mdx | 183 +++++- .../how-high-availability-works.mdx | 105 +++- ...rying-the-cluster-in-high-availability.mdx | 42 +- .../auth-system-integrations.mdx | 18 + .../query-privileges.mdx | 31 +- .../role-based-access-control.mdx | 17 +- .../enabling-memgraph-enterprise.mdx | 6 + pages/release-notes.mdx | 34 + 11 files changed, 1011 insertions(+), 23 deletions(-) create mode 100644 pages/clustering/high-availability/coordinator-authentication.mdx diff --git a/pages/clustering/high-availability.mdx b/pages/clustering/high-availability.mdx index be6a01e31..19f83d8e3 100644 --- a/pages/clustering/high-availability.mdx +++ b/pages/clustering/high-availability.mdx @@ -49,6 +49,11 @@ recommended configuration patterns. Recommended practices for running a robust, reliable, and well-observed HA deployment. +### [Coordinator authentication](/clustering/high-availability/coordinator-authentication) + +Secure the cluster's control plane with single sign-on, Raft-replicated +coordinator roles, and the `COORDINATOR_READ` / `COORDINATOR_WRITE` privileges. + ### [HA commands reference guide](/clustering/high-availability/ha-commands-reference) A complete reference of all commands for managing coordinators, registering diff --git a/pages/clustering/high-availability/_meta.ts b/pages/clustering/high-availability/_meta.ts index f09ece7ce..23e5c8ca2 100644 --- a/pages/clustering/high-availability/_meta.ts +++ b/pages/clustering/high-availability/_meta.ts @@ -1,6 +1,7 @@ export default { "how-high-availability-works": "Under the hood", "querying-the-cluster-in-high-availability": "Querying the cluster in HA", + "coordinator-authentication": "Coordinator authentication", "setup-ha-cluster-docker": "Set up HA cluster with Docker", "setup-ha-cluster-docker-compose": "Set up HA cluster with Docker Compose", "setup-ha-cluster-k8s": "Set up HA cluster with K8s", diff --git a/pages/clustering/high-availability/coordinator-authentication.mdx b/pages/clustering/high-availability/coordinator-authentication.mdx new file mode 100644 index 000000000..9b0907c8f --- /dev/null +++ b/pages/clustering/high-availability/coordinator-authentication.mdx @@ -0,0 +1,592 @@ +--- +title: Coordinator authentication and authorization +description: Learn how to secure Memgraph high availability coordinators with single sign-on, Raft-replicated roles and the COORDINATOR_READ and COORDINATOR_WRITE privileges. +--- + +import { Callout } from 'nextra/components' +import { Steps } from 'nextra/components' +import {CommunityLinks} from '/components/social-card/CommunityLinks' + +# Coordinator authentication and authorization Enterprise + +Coordinators are the control plane of a [high availability +cluster](/clustering/high-availability): they register and unregister data +instances, promote and demote MAIN, hand out the routing table, and change +cluster-wide settings. From Memgraph 3.13, that control plane can be protected +with [single sign-on (SSO)](/database-management/authentication-and-authorization/auth-system-integrations#single-sign-on) +against your corporate identity provider (IdP), backed by a small set of +Raft-replicated roles. + + +SSO authentication on coordinators, coordinator role management, privilege +grants and privilege enforcement are **Memgraph Enterprise** features and +require a valid license. Without a valid license, coordinators keep the +pre-3.13 behavior: username/password connections are accepted as a passthrough +with full access. + + +Before continuing, read [how high availability +works](/clustering/high-availability/how-high-availability-works) and [querying +the cluster in high +availability](/clustering/high-availability/querying-the-cluster-in-high-availability). + +## How coordinator auth differs from data instances + +Coordinators do **not** store users. There is no auth key-value store on a +coordinator, no `CREATE USER`, no passwords, and no fine-grained access control. +The only authorization state a coordinator keeps is a list of **roles**, each +carrying a coordinator privilege mask. + +| | Data instance | Coordinator | +|---|---|---| +| Users | Stored in the auth store | Not supported | +| Roles | Stored in the auth store | Stored in the **Raft-replicated cluster state** | +| Privileges | Full privilege set + fine-grained access control | Exactly two: `COORDINATOR_READ`, `COORDINATOR_WRITE` | +| Basic auth | Username and password are validated | Passthrough — credentials are ignored (see [below](#basic-authentication-passthrough)) | +| SSO | Supported (OIDC, SAML, Kerberos) | Supported (OIDC, SAML, Kerberos) | + +Because roles live in the Raft log, they survive coordinator restarts, are +replicated to every coordinator, are reconstructed by a follower that rejoins +after being down, and survive leader failover. + + +**Breaking change in Memgraph 3.13:** the `COORDINATOR` privilege has been +removed. It never gated any operation. It is no longer accepted in `GRANT` / +`DENY` / `REVOKE` statements and is no longer reported by `SHOW PRIVILEGES`. A +stale `COORDINATOR` grant in an existing deployment is simply never reported and +never checked — no migration is required. Coordinator access is now controlled +by the new `COORDINATOR_READ` and `COORDINATOR_WRITE` privileges described +below, which are granted **on the coordinators themselves**, not on data +instances. + + +## Coordinator privileges + +Coordinators enforce exactly two privileges: + +| Privilege | Grants | +|---|---| +| `COORDINATOR_READ` | Reading the routing table and all read-only introspection queries. | +| `COORDINATOR_WRITE` | Every query runnable on a coordinator. `COORDINATOR_WRITE` is a **superset** of `COORDINATOR_READ`. | + +These names were chosen because bare `READ` and `WRITE` already exist as +data-instance fine-grained privileges. They are meaningful **only on +coordinators** and are deliberately excluded from the data-instance privilege +set, so `GRANT ALL PRIVILEGES` on a data instance does not grant them. + +A session's effective privilege is the **union** of the masks of all its roles. +A role with no grant confers nothing. + +### Which privilege each query requires + +Read-only introspection requires `COORDINATOR_READ`; every mutating or +administrative query requires `COORDINATOR_WRITE`. A `COORDINATOR_WRITE` grant +satisfies a `COORDINATOR_READ` requirement, but not the other way round. + +| Query | Required privilege | +|---|---| +| Routing table (Bolt `ROUTE` message) | `COORDINATOR_READ` | +| [`SHOW INSTANCE`](/clustering/high-availability/ha-commands-reference#show-instance) | `COORDINATOR_READ` | +| [`SHOW INSTANCES`](/clustering/high-availability/ha-commands-reference#show-instances) | `COORDINATOR_READ` | +| [`SHOW COORDINATOR SETTINGS`](/clustering/high-availability/ha-commands-reference#coordinator-runtime-settings) | `COORDINATOR_READ` | +| [`SHOW REPLICATION LAG`](/clustering/high-availability/ha-commands-reference#show-replication-lag) | `COORDINATOR_READ` | +| `SHOW ROLES` | `COORDINATOR_READ` | +| `SHOW PRIVILEGES FOR ROLE ` | `COORDINATOR_READ` | +| `SHOW CONFIG`, `SHOW SETTING`, system info queries | `COORDINATOR_READ` | +| `SHOW CURRENT USER`, `SHOW CURRENT ROLE` | **None** (self-service, see [below](#self-service-identity-queries)) | +| [`REGISTER INSTANCE`](/clustering/high-availability/ha-commands-reference#register-instance) / [`UNREGISTER INSTANCE`](/clustering/high-availability/ha-commands-reference#unregister-instance) | `COORDINATOR_WRITE` | +| [`SET INSTANCE ... TO MAIN`](/clustering/high-availability/ha-commands-reference#set-instance--to-main) / [`DEMOTE INSTANCE`](/clustering/high-availability/ha-commands-reference#demote-instance) | `COORDINATOR_WRITE` | +| [`ADD COORDINATOR`](/clustering/high-availability/ha-commands-reference#add-coordinator) / [`REMOVE COORDINATOR`](/clustering/high-availability/ha-commands-reference#remove-coordinator) | `COORDINATOR_WRITE` | +| [`UPDATE CONFIG`](/clustering/high-availability/ha-commands-reference#update-config) | `COORDINATOR_WRITE` | +| [`YIELD LEADERSHIP`](/clustering/high-availability/ha-commands-reference#yield-leadership) | `COORDINATOR_WRITE` | +| [`SET COORDINATOR SETTING`](/clustering/high-availability/ha-commands-reference#coordinator-runtime-settings) | `COORDINATOR_WRITE` | +| [`FORCE RESET CLUSTER STATE`](/clustering/high-availability/ha-commands-reference#force-reset-cluster-state) | `COORDINATOR_WRITE` | +| `SET SETTING` | `COORDINATOR_WRITE` | +| [`RELOAD BOLT_SERVER TLS` / `RELOAD INTRA_CLUSTER TLS`](/database-management/ssl-encryption#reload-ssl-certificates-at-runtime) | `COORDINATOR_WRITE` | +| `CREATE ROLE`, `DROP ROLE`, `GRANT`, `REVOKE` | `COORDINATOR_WRITE` | + +Anything not recognized fails closed and requires `COORDINATOR_WRITE`. + +## Authentication modes + +A coordinator accepts exactly two kinds of Bolt connection: **basic/none** and +an **SSO scheme listed in `--auth-module-mappings`**. Any other scheme is +rejected with: + +``` +The "" authentication scheme isn't supported on this coordinator; +connect with basic auth or an SSO scheme listed in the auth-module-mappings flag. +``` + +### Basic authentication passthrough + +When SSO is not in effect, connecting with a username and password (or with no +auth at all) **succeeds and the credentials are ignored**. The session gets full +`COORDINATOR_WRITE` access. This is the pre-3.13 behavior, it requires no +license, and it keeps existing admin tooling working unchanged. + +Basic/none authentication is **denied** only when **all three** of the following +hold: + +1. SSO is configured — `--auth-module-mappings` is non-empty. +2. The enterprise license is valid. +3. The committed role set contains at least one role holding + `COORDINATOR_WRITE`. + +In that case the connection is rejected with: + +``` +Basic authentication is disabled on this coordinator because SSO is configured; +connect with an SSO scheme listed in the auth-module-mappings flag. +``` + + +**Anybody can log in until a `COORDINATOR_WRITE` role exists.** Condition 3 is +what makes coordinator SSO self-bootstrapping. On a coordinator that starts with +`--auth-module-mappings` set but an empty role set, SSO cannot yet grant a +privileged session to anybody — so basic auth stays open, and you use it to +create the first role and grant it `COORDINATOR_WRITE`. The moment that grant +commits to Raft, basic auth closes on the next login attempt and SSO takes over. +**No coordinator restart is required.** + +The same rule applies in reverse: if you drop or revoke the last +`COORDINATOR_WRITE` role on a live cluster, basic auth reopens rather than +leaving the cluster unadministrable. + + + +**Break-glass on license loss.** If the enterprise license is missing, expired +or invalid, SSO rejects every login. Condition 2 above means basic auth falls +back to the passthrough in exactly that case, so a license transition can never +lock every Bolt session out of a coordinator. Use that session to re-install the +license over Bolt: + +```cypher +SET DATABASE SETTING 'enterprise.license' TO 'License'; +SET DATABASE SETTING 'organization.name' TO 'Organization'; +``` + +The license check for this decision is the full, non-cached check — a license +that expires by date takes effect immediately rather than at the next cache +refresh. + + +### A follower without quorum cannot be logged into + +Both the basic-auth decision and the SSO role check need the **leader's** +committed role set, and both are **fail-closed**: when the leader cannot be +reached, the login is rejected rather than validated against possibly-stale +local replicated state, which could still list a dropped role or an +already-revoked privilege mask. + +So on a coordinator that has lost quorum (it is a follower, no leader is +elected, or the leader is unreachable) and has SSO configured with a valid +license: + +- **SSO logins are rejected** with: + + ``` + SSO authentication failed: the coordinator leader is unreachable, so roles + can't be validated. Retry once a leader is elected. + ``` + +- **Basic/none logins are also rejected.** An unknown role set is not treated as + "no writable role", so the break-glass path does not open during a transient + leader outage. + +This is intentional and temporary: SSO is unavailable in that window anyway, and +access returns as soon as a leader is elected. If SSO is **not** configured, the +basic-auth passthrough never contacts the leader, so it keeps working on a +partitioned follower. + +### SSO authentication + +For a scheme present in `--auth-module-mappings`, the coordinator runs the +corresponding auth module and genuinely authenticates the connection. The same +[built-in and custom auth +modules](/database-management/authentication-and-authorization/auth-system-integrations) +used on data instances work here, and `MEMGRAPH_SSO_*` environment variables are +inherited by the module subprocess exactly as they are on data instances — there +is no separate coordinator-side SSO configuration. + +The login is accepted only when **all** of the following hold: + +1. The module authenticates the identity (valid, unexpired IdP token). +2. The module returns at least one role. +3. **Every** role the module returns exists in the coordinator's committed role + set. A multi-role response succeeds only when all of its roles exist. +4. The union of those roles' masks grants at least `COORDINATOR_READ`. + + +**A role without privileges cannot be used to log in.** If the roles all exist +but none of them has been granted `COORDINATOR_READ` or `COORDINATOR_WRITE`, the +connection is **rejected at login** rather than admitted as a session that would +be denied every query — including the routing table. Grant a privilege to the +role before mapping identities onto it. + + +Each rejection reason has its own message, so an operator rolling SSO out can +tell a bad token apart from a misconfigured role mapping: + +| Situation | Error returned to the client | +|---|---| +| Invalid/expired token, module failure, or missing license | `SSO authentication failed: the identity provider token was rejected, the auth module failed, or the enterprise license is missing.` | +| Module returned no roles | `SSO authentication failed: the identity provider returned no roles for this identity. Map the identity's group to a coordinator role.` | +| A returned role does not exist on the coordinator | `SSO authentication failed: the identity provider returned a role that doesn't exist on this coordinator. Create it with CREATE ROLE, or fix the identity provider mapping.` | +| Roles exist but carry no privilege | `SSO authentication failed: this identity's role(s) exist but carry no coordinator privilege. Grant COORDINATOR_READ or COORDINATOR_WRITE to one of them.` | +| Leader unreachable | `SSO authentication failed: the coordinator leader is unreachable, so roles can't be validated. Retry once a leader is elected.` | + +The offending role name is written to the coordinator log, **not** returned to +the client, so a rejected login cannot be used to enumerate the coordinator's +role set. + +The username the module reports is recorded as the session principal and is used +for [audit logging](/database-management/logs) and `SHOW CURRENT USER`. A module +that omits the username still logs in — the coordinator authorizes by role — but +its queries cannot be attributed to a person, and a warning is logged once at +login. + +## Managing coordinator roles and privileges + +All role and privilege queries are documented in the [HA reference +commands](/clustering/high-availability/ha-commands-reference#coordinator-role-and-privilege-management) +guide. In short: + +```cypher +CREATE ROLE ops; +CREATE ROLE IF NOT EXISTS ops; +DROP ROLE ops; +SHOW ROLES; + +GRANT COORDINATOR_READ TO ops; +GRANT COORDINATOR_WRITE TO ops; +GRANT ALL PRIVILEGES TO ops; -- grants both coordinator privileges +REVOKE COORDINATOR_WRITE FROM ops; +REVOKE ALL PRIVILEGES FROM ops; -- removes both + +SHOW PRIVILEGES FOR ROLE ops; +``` + +All of these can be run on **any** coordinator: on a follower they are +transparently forwarded to the leader. Writes are committed through the Raft +log; `SHOW ROLES` and `SHOW PRIVILEGES FOR ROLE` are strong reads served by the +leader. + +### Self-service identity queries + +`SHOW CURRENT USER` and `SHOW CURRENT ROLE` are exempt from the privilege check +and from the license gate — they only reveal the session's own identity, so even +a session whose roles were revoked mid-flight can still inspect who it is. + +- `SHOW CURRENT USER` returns the principal the identity provider authenticated. + It is purely session-local and works even when the leader is unreachable. A + basic-auth passthrough session authenticated no principal and returns `null`. +- `SHOW CURRENT ROLE` returns the session's roles **filtered against the + leader's committed role set**, so it stops naming a role that `DROP ROLE` + already removed. A basic-auth passthrough session has no roles and returns + `null`. + +### Auth queries rejected on coordinators + +Everything outside the small surface above is rejected with: + +``` +Coordinator can run only coordinator queries! +``` + +That includes: + +- User management: `CREATE USER`, `SET PASSWORD`, `SHOW USERS`, `SET ROLE`, + `GRANT ROLE`. +- `DENY` in **any** form. +- `GRANT` / `REVOKE` targeting a `USER` (`GRANT COORDINATOR_READ TO USER foo`). +- Privilege lists containing any non-coordinator privilege + (`GRANT MATCH TO ops`). +- [Fine-grained access + control](/database-management/authentication-and-authorization/role-based-access-control#fine-grained-access-control) + — `GRANT ... ON NODES ...` / `ON EDGES ...` — coordinators have no graph. +- Property permissions. +- [Multi-tenancy](/database-management/multi-tenancy) database access: + `GRANT DATABASE`, `REVOKE DATABASE`, `SET MAIN DATABASE`. +- `SHOW PRIVILEGES FOR USER `. +- `SHOW PRIVILEGES FOR ROLE ` with a trailing `ON MAIN`, `ON CURRENT` or + `ON DATABASE ` clause — coordinators have no databases. + +## Privileges are re-checked on every query + +An SSO session does **not** cache the privilege mask it was given at login. On +**every** query — and on every routing-table request — the coordinator +re-derives the session's effective mask from the **leader's** committed role set: +if this coordinator is the ready leader it reads locally, otherwise it sends a +`GetRolesRpc` to the leader. + +This has three consequences worth planning for: + +- **`REVOKE` and `DROP ROLE` take effect immediately**, without the client + reconnecting. Long-lived connections — driver routing pools, open admin shells + — are downgraded on their very next query. A session whose roles were all + dropped keeps its connection but is denied every privileged query. +- **A leader outage denies queries on SSO sessions.** With no readable role set + the effective mask is empty (fail-closed), so queries fail until a leader is + elected. Retry once `SHOW INSTANCES` reports a leader again. +- **Basic-auth passthrough sessions are unaffected.** They carry no roles, so + they keep their login-time full `COORDINATOR_WRITE` mask and never contact the + leader for a privilege check. + +Denied queries fail with: + +``` +You don't have the required privilege to run this query on the coordinator! +``` + +and a denied routing-table request fails with: + +``` +You don't have permission to read the routing table on the coordinator! +``` + +The routing-table denial is reported as a non-retryable client error, so drivers +do not retry it as if it were a transient failure. + +## Bolt+routing with SSO + + +**A `neo4j://` routing connection works only if the same roles exist on both the +coordinators and the data instances.** + + +[Bolt+routing](/clustering/high-availability/querying-the-cluster-in-high-availability) +is entirely client-side, and the driver reuses **one set of credentials for both +legs** of the connection. With SSO in the picture, a single `neo4j://` session +therefore performs two independent authentications: + + + +{

The driver authenticates against a coordinator

} + +It sends the SSO scheme and IdP token to a coordinator and issues a `ROUTE` +message. The coordinator runs the auth module, requires every returned role to +exist **in the Raft-replicated coordinator role set**, and requires the union of +their masks to grant at least `COORDINATOR_READ` — otherwise the routing table +request is denied. + +{

The driver authenticates against a data instance

} + +Using the routing table, the driver opens a connection to MAIN or a REPLICA with +the **same scheme and the same token**. The data instance runs its own auth +module and requires every returned role to exist **in its auth store**, with +whatever data privileges (`MATCH`, `CREATE`, …) the query needs. + +
+ +For that to work end to end: + +| Requirement | Why | +|---|---| +| The **same SSO scheme** is listed in `--auth-module-mappings` on coordinators *and* on every data instance | The driver sends one scheme to both; a scheme absent from a node's mappings is rejected there. | +| The **same role names** the IdP returns exist on coordinators *and* on data instances | Each side validates the returned roles against its own role store, and any missing role rejects the whole login on that side. | +| Those roles hold `COORDINATOR_READ` (or `COORDINATOR_WRITE`) **on the coordinators** | Otherwise the `ROUTE` message is denied and the driver never obtains a routing table. | +| Those roles hold the needed **data privileges on the data instances** | The coordinator privileges are not visible to, and mean nothing on, a data instance. | + +The privilege *values* are necessarily different on the two sides — coordinators +know only `COORDINATOR_READ` / `COORDINATOR_WRITE`, data instances know only the +data privilege set — but the **role names must match**. A typical setup mirrors +each IdP group into a role of the same name on both sides: + +```cypher +-- On any coordinator +CREATE ROLE analyst; +GRANT COORDINATOR_READ TO analyst; + +CREATE ROLE dba; +GRANT ALL PRIVILEGES TO dba; -- COORDINATOR_READ + COORDINATOR_WRITE +``` + +```cypher +-- On the MAIN data instance (replicated to REPLICAs) +CREATE ROLE analyst; +GRANT MATCH TO analyst; + +CREATE ROLE dba; +GRANT ALL PRIVILEGES TO dba; -- the full data-instance privilege set +``` + + +A role that exists on the data instances but not on the coordinators produces a +confusing failure mode: direct `bolt://` connections to MAIN work, but every +`neo4j://` routing connection fails at the coordinator. Check `SHOW ROLES` on a +coordinator and `SHOW ROLES` on MAIN and reconcile the two lists. + + +## Rolling SSO out + +The bootstrap order differs between coordinators and data instances, and getting +it wrong is the most common way to lock yourself out. + +### On coordinators — no restart needed + +Coordinators can be started with `--auth-module-mappings` from the very +beginning. Because basic auth stays open until a `COORDINATOR_WRITE` role +exists, you can create the roles over Bolt on the running cluster. + + + +{

Start the coordinators with the SSO mapping

} + +``` +--auth-module-mappings=oidc-entra-id +``` + +Set the `MEMGRAPH_SSO_*` environment variables the module needs, exactly as you +would on a data instance. + +{

Connect with basic auth

} + +No `COORDINATOR_WRITE` role exists yet, so username/password (or no credentials +at all) is still accepted with full access: + +``` +mgconsole --host --port 7687 +``` + +{

Create the roles and grant privileges

} + +```cypher +CREATE ROLE dba; +GRANT ALL PRIVILEGES TO dba; + +CREATE ROLE analyst; +GRANT COORDINATOR_READ TO analyst; +``` + +The role names must match what your IdP returns — see [role +mapping](/database-management/authentication-and-authorization/auth-system-integrations#role-mapping). + +{

SSO is now enforced

} + +As soon as `GRANT ALL PRIVILEGES TO dba` commits to Raft, all three deny +conditions hold and the **next** basic-auth login attempt is rejected. Existing +basic-auth sessions keep their full mask until they disconnect. Verify with: + +```cypher +SHOW ROLES; +SHOW PRIVILEGES FOR ROLE dba; +``` + +
+ +### On data instances — restart required + +Data instances have no equivalent escape hatch. Their SSO path validates the +IdP's roles against the auth store, and a role that does not exist means the +login fails. If you enable the module before the roles exist, SSO users cannot +log in and — when the mapped scheme is `basic` (LDAP) — username/password +authentication is delegated to the module too, so you may have no way in at all +to create them. + +The safe order is therefore: + + + +{

Start the data instance *without* `--auth-module-mappings`

} + +{

Log in and create the roles

} + +```cypher +CREATE ROLE dba; +GRANT ALL PRIVILEGES TO dba; + +CREATE ROLE analyst; +GRANT MATCH TO analyst; +``` + +{

Restart the data instance with `--auth-module-mappings` set

} + +SSO logins now find their roles and succeed. + +
+ + +In containerized deployments you can collapse this into a single start by +creating the roles with +[`--init-file`](/database-management/authentication-and-authorization/auth-system-integrations#docker-deployment-note), +which runs a Cypher script before the database begins serving connections. That +avoids the stop-and-restart cycle entirely. + + + +Do the coordinator side and the data-instance side with the **same role names**, +or [Bolt+routing](#boltrouting-with-sso) connections will fail even though +direct connections to each node work. + + +## Rolling upgrades + +The new role and privilege queries are carried by new coordinator-to-coordinator +RPCs. There is no RPC version negotiation. During a rolling upgrade, if a +new follower forwards a role or privilege query to a leader that has not been +upgraded yet, the query **fails with an error** rather than crashing the +coordinator: + +``` +Query forwarded to the leader but it failed to process the request! +Check the logs on the leader to find out what happened. +``` + +Complete the upgrade of all coordinators before managing roles, or run the role +queries directly against an upgraded leader. + +The Raft cluster state itself is version-safe in both directions: an older +coordinator ignores the unknown roles key, and a newer coordinator reading an +older log or snapshot sees an empty role set. No log store version bump is +involved. + +## Timeouts + +Role and privilege queries — like all forwarded coordinator queries — run on the +caller's Bolt session thread, so they carry explicit RPC timeouts to prevent a +session blocking forever against a reachable-but-stuck leader. See [RPC +timeouts](/clustering/high-availability/how-high-availability-works#rpc-timeouts) +for the full table. The values relevant to authentication are: + +| RPC | Timeout | Used by | +|---|---|---| +| `GetRolesReq` | 10s | SSO login role validation, the per-query privilege re-check, `SHOW ROLES`, `SHOW CURRENT ROLE` | +| `GetRolePrivilegesReq` | 10s | `SHOW PRIVILEGES FOR ROLE` | +| `CreateRoleReq` / `DropRoleReq` | 10s | `CREATE ROLE`, `DROP ROLE` | +| `GrantPrivilegeReq` / `RevokePrivilegeReq` | 10s | `GRANT`, `REVOKE` | + +Each of these budgets covers a Raft commit, which is itself capped at 3 seconds, +plus headroom. Hitting the timeout surfaces as a query error; retry the +operation. + +Independently of the RPC timeout, the auth module subprocess is bounded by +[`--auth-module-timeout-ms`](/database-management/authentication-and-authorization/auth-system-integrations#configuration-flags), +the same flag used on data instances. A module that exceeds it fails the login. + +## Limitations + +- **No users on coordinators.** Only the role set and each role's privilege mask + are persisted. There is no way to create a coordinator-local account. +- **No SSO auto-provisioning of roles.** Unlike some data-instance setups, an + SSO identity does not implicitly create `readonly` / `readwrite` / `admin` + roles on a coordinator. Roles must be created explicitly with `CREATE ROLE`. +- **Only two privileges.** The fine-grained data-instance privilege set is not + modeled on coordinators. +- **Role names** must match the + [`--auth-user-or-role-name-regex`](/database-management/configuration) pattern, + the same as on data instances. + +## Troubleshooting + +| Symptom | Likely cause | +|---|---| +| Basic auth suddenly rejected after a `GRANT` | Expected — a `COORDINATOR_WRITE` role now exists, so SSO is enforced. Connect with an SSO scheme. | +| Basic auth unexpectedly accepted while SSO is configured | Either the license is invalid, or no role holds `COORDINATOR_WRITE`. Check `SHOW PRIVILEGES FOR ROLE` and the license status. | +| Every login rejected on one coordinator | That coordinator cannot reach a leader. Check `SHOW INSTANCES` from another coordinator and wait for the election to finish. | +| SSO login rejected with "role that doesn't exist" | `CREATE ROLE` on the coordinator, or fix the IdP group mapping. Check `SHOW ROLES`. | +| SSO login rejected with "carry no coordinator privilege" | `GRANT COORDINATOR_READ` or `GRANT COORDINATOR_WRITE` to the role. | +| `neo4j://` fails but `bolt://` to MAIN works | The role exists on the data instances but not on the coordinators, or it lacks `COORDINATOR_READ`. | +| A session was working and now every query is denied | Its roles were revoked or dropped — privileges are re-checked on every query. Reconnect after fixing the grant. | + + diff --git a/pages/clustering/high-availability/ha-commands-reference.mdx b/pages/clustering/high-availability/ha-commands-reference.mdx index 40afbf51d..5f76a92ff 100644 --- a/pages/clustering/high-availability/ha-commands-reference.mdx +++ b/pages/clustering/high-availability/ha-commands-reference.mdx @@ -28,6 +28,17 @@ LEADERSHIP`](#yield-leadership), which must be run directly on the current leader. + +From Memgraph 3.13, coordinators can enforce privileges on these queries. Every +query on this page requires either `COORDINATOR_READ` (read-only introspection) +or `COORDINATOR_WRITE` (everything mutating). A session that connected with +basic auth carries full `COORDINATOR_WRITE`, so nothing changes unless you +enable [SSO on coordinators](/clustering/high-availability/coordinator-authentication). +See the [privilege +reference](/clustering/high-availability/coordinator-authentication#which-privilege-each-query-requires) +for the per-query mapping. + + ### `ADD COORDINATOR` Adds a coordinator to the cluster. @@ -332,7 +343,9 @@ informational notification that the request was submitted. - Failover of data instances is not triggered, but the new leader recomputes cluster state, so a cluster that was already missing a MAIN can fail over as part of the leadership change. -- No privilege is required to run this query on the coordinators. +- Requires `COORDINATOR_WRITE`. A basic-auth session carries it implicitly; an + [SSO session](/clustering/high-availability/coordinator-authentication) needs + a role that has been granted it. {

Typical use cases

} @@ -553,7 +566,11 @@ survives coordinator restarts and leader re-elections, and is honored across failovers: a newly promoted MAIN comes up read-only when the cluster is in read-only mode, instead of silently accepting writes. -No privilege is required to run this query on the coordinators. +`SET COORDINATOR SETTING` requires `COORDINATOR_WRITE` and `SHOW COORDINATOR +SETTINGS` requires `COORDINATOR_READ`. A basic-auth session carries both +implicitly; an [SSO +session](/clustering/high-availability/coordinator-authentication) needs a role +that has been granted them. Enabling read-only mode blocks **all write sources** on the MAIN — user Cypher @@ -575,10 +592,170 @@ cluster self-heals to the requested state. +## Coordinator role and privilege management + + +These queries are **Memgraph Enterprise** features and require a valid license. +They exist so that [SSO +identities](/clustering/high-availability/coordinator-authentication) have +something to map onto. Coordinators have no users — only roles. + + +Coordinator roles are stored in the **Raft-replicated cluster state**, not in +the auth store, so they survive restarts, follower catch-up and leader failover. +Like the cluster registration queries, all of these can be run on any +coordinator: writes are transparently forwarded to the leader and committed +through the Raft log, and `SHOW ROLES` / `SHOW PRIVILEGES FOR ROLE` are strong +reads served by the leader. + +### `CREATE ROLE` + +Creates a coordinator role. New roles start with **no** privileges. + +```cypher +CREATE ROLE ifNotExists? roleName; +``` + +{

Behavior & implications

} + +- Errors if the role already exists, unless `IF NOT EXISTS` is given. +- The role name must match the `--auth-user-or-role-name-regex` pattern, + otherwise the query fails with `Invalid role name ''.` +- Requires `COORDINATOR_WRITE`. + +{

Example

} + +```cypher +CREATE ROLE dba; +CREATE ROLE IF NOT EXISTS analyst; +``` + +### `DROP ROLE` + +Removes a coordinator role. + +```cypher +DROP ROLE roleName; +``` + +{

Behavior & implications

} + +- Errors with `Role '' doesn't exist.` if the role is not present. +- Takes effect on **already-connected sessions immediately** — privileges are + re-derived from the committed role set on every query, so a session that + authenticated with the dropped role is denied its next privileged query + without needing to reconnect. +- Requires `COORDINATOR_WRITE`. + +{

Example

} + +```cypher +DROP ROLE analyst; +``` + +### `SHOW ROLES` + +Lists the coordinator roles. Returns one `role` column, name only. + +```cypher +SHOW ROLES; +``` + +{

Behavior & implications

} + +- Strong read served by the leader. If no leader can be reached, the query fails + rather than returning possibly-stale local state. +- Requires `COORDINATOR_READ`. + +### `GRANT` / `REVOKE` coordinator privileges + +Grants or revokes a coordinator privilege on a role. Coordinators support +exactly two privileges: `COORDINATOR_READ` and `COORDINATOR_WRITE`, where +`COORDINATOR_WRITE` is a superset of `COORDINATOR_READ`. + +```cypher +GRANT ( ALL PRIVILEGES | COORDINATOR_READ | COORDINATOR_WRITE [, ...] ) TO ROLE? roleName; +REVOKE ( ALL PRIVILEGES | COORDINATOR_READ | COORDINATOR_WRITE [, ...] ) FROM ROLE? roleName; +``` + +{

Behavior & implications

} + +- `GRANT ALL PRIVILEGES` grants **both** coordinator privileges; + `REVOKE ALL PRIVILEGES` removes both. +- Errors with `Role '' doesn't exist.` if the role is not present. +- Only `COORDINATOR_READ` and `COORDINATOR_WRITE` may appear in the privilege + list. Any other privilege, `DENY` in any form, a `USER` target, fine-grained + access control (`ON NODES` / `ON EDGES`), property permissions and + `GRANT DATABASE` are all rejected on a coordinator. +- Like `DROP ROLE`, a `REVOKE` applies to already-connected sessions on their + next query. +- Requires `COORDINATOR_WRITE`. + +{

Example

} + +```cypher +GRANT ALL PRIVILEGES TO dba; +GRANT COORDINATOR_READ TO analyst; +REVOKE COORDINATOR_WRITE FROM analyst; +``` + +### `SHOW PRIVILEGES FOR ROLE` + +Reports the privileges granted to a coordinator role, one per row. + +```cypher +SHOW PRIVILEGES FOR ROLE? roleName; +``` + +{

Behavior & implications

} + +- A role with no grants returns no rows. +- The trailing `ON MAIN | CURRENT | DATABASE ` clause is **rejected** — + coordinators have no databases. +- `SHOW PRIVILEGES FOR USER ` is rejected — coordinators have no users. +- Strong read served by the leader. +- Requires `COORDINATOR_READ`. + +{

Example

} + +```cypher +SHOW PRIVILEGES FOR ROLE dba; +``` + +```plaintext ++---------------------+ +| privilege | ++---------------------+ +| COORDINATOR_READ | +| COORDINATOR_WRITE | ++---------------------+ +``` + +### `SHOW CURRENT USER` and `SHOW CURRENT ROLE` + +Report the identity of the current session. + +```cypher +SHOW CURRENT USER; +SHOW CURRENT ROLE; +``` + +{

Behavior & implications

} + +- **No privilege and no license are required** — these are self-service queries + that reveal only the session's own identity. +- `SHOW CURRENT USER` returns the principal the SSO module reported. It is + session-local and works even when the leader is unreachable. A basic-auth + passthrough session returns `null`. +- `SHOW CURRENT ROLE` returns the session's roles filtered against the leader's + committed role set, so a dropped role stops being reported. A basic-auth + passthrough session has no roles and returns `null`. + ## Error handling If a Raft log commit fails for any cluster operation (register, unregister, -promote, demote, add coordinator), the error message will indicate: +promote, demote, add coordinator, role or privilege change), the error message +will indicate: > Writing to Raft log failed. Please retry the operation. diff --git a/pages/clustering/high-availability/how-high-availability-works.mdx b/pages/clustering/high-availability/how-high-availability-works.mdx index 0773a4442..782f53cd4 100644 --- a/pages/clustering/high-availability/how-high-availability-works.mdx +++ b/pages/clustering/high-availability/how-high-availability-works.mdx @@ -255,24 +255,109 @@ in the cluster to ensure high availability, with timeouts. | RPC message request | source | target | timeout | |--------------------------|-------------|----------------| -----------------| -| `ShowInstancesReq` | Coordinator | Coordinator | | -| `DemoteMainToReplicaReq` | Coordinator | Data instance | | -| `PromoteToMainReq` | Coordinator | Data instance | | -| `RegisterReplicaOnMainReq` | Coordinator | Data instance | | -| `UnregisterReplicaReq` | Coordinator | Data instance | | -| `EnableWritingOnMainReq` | Coordinator | Data instance | deprecated | -| `GetDatabaseHistoriesReq` | Coordinator | Data instance | | +| `ShowInstancesReq` | Coordinator | Coordinator | 10s | +| `DemoteMainToReplicaReq` | Coordinator | Data instance | 10s | +| `PromoteToMainReq` | Coordinator | Data instance | 10s | +| `RegisterReplicaOnMainReq` | Coordinator | Data instance | 10s | +| `UnregisterReplicaReq` | Coordinator | Data instance | 10s | +| `ReplicationLagReq` | Coordinator | Data instance | 5s | +| `GetDatabaseHistoriesReq` | Coordinator | Data instance | 10s | | `StateCheckReq` | Coordinator | Data instance | 5s | -| `SwapMainUUIDReq` | Coordinator | Data instance | | +| `SwapMainUUIDReq` | Coordinator | Data instance | 10s | +| `UpdateDataInstanceConfigReq` | Coordinator | Data instance | 10s | | `FrequentHeartbeatReq` | Main | Replica | 5s | -| `HeartbeatReq` | Main | Replica | | -| `SystemRecoveryReq` | Main | Replica | 5s | +| `HeartbeatReq` | Main | Replica | 10s | +| `SystemRecoveryReq` | Main | Replica | 30s | | `PrepareCommitRpc` | Main | Replica | proportional | | `FinalizeCommitReq` | Main | Replica | 10s | | `SnapshotRpc` | Main | Replica | proportional | | `WalFilesRpc` | Main | Replica | proportional | | `CurrentWalRpc` | Main | Replica | proportional | + +`EnableWritingOnMainReq` was **removed in Memgraph 3.13**. It was never sent — +writing on a newly promoted MAIN is enabled through the `writing_enabled` flag +carried inside `PromoteToMainRpc`. Its Prometheus counters were removed along +with it. + + +{
Follower-to-leader forwarding timeouts
} + +Cluster management queries can be run on any coordinator; a follower forwards +them to the leader over RPC. From Memgraph 3.13, each of these RPCs has an +explicit timeout. They run on the caller's **Bolt session thread**, so without +one, a session would block forever against a leader that is reachable but stuck. + +The four instance operations must outlast the work they trigger on the leader, +or a follower would report failure for an operation the leader has already +committed to Raft. Their budgets are the sum of that work plus headroom, where a +Raft commit is capped at 3 seconds and each RPC from the leader to a data +instance is capped by its entry in the table above. + +| RPC message request | source | target | timeout | Budget breakdown | +|--------------------------|-------------|-------------|---------|------------------| +| `RegisterInstanceReq` | Coordinator | Coordinator | 30s | Raft commit + demote the new replica + register it | +| `UnregisterInstanceReq` | Coordinator | Coordinator | 20s | Raft commit + one RPC to the current MAIN | +| `DemoteInstanceReq` | Coordinator | Coordinator | 20s | Raft commit + one RPC to the current MAIN | +| `SetInstanceToMainReq` | Coordinator | Coordinator | 60s | Raft commit + one `SwapMainUUID` per other instance + promote the new MAIN | +| `AddCoordinatorReq` | Coordinator | Coordinator | 10s | Raft commit only | +| `RemoveCoordinatorReq` | Coordinator | Coordinator | 10s | Raft commit only | +| `UpdateConfigReq` | Coordinator | Coordinator | 10s | Raft commit only | +| `ForceResetReq` | Coordinator | Coordinator | 60s | Unbounded leader-side work — see the note below | +| `SetCoordinatorSettingReq` | Coordinator | Coordinator | 10s | Raft commit only | +| `GetRoutingTableReq` | Coordinator | Coordinator | 10s | Read served by the leader | +| `CoordReplLagReq` | Coordinator | Coordinator | 10s | Read served by the leader | +| `CreateRoleReq` | Coordinator | Coordinator | 10s | Raft commit only | +| `DropRoleReq` | Coordinator | Coordinator | 10s | Raft commit only | +| `GrantPrivilegeReq` | Coordinator | Coordinator | 10s | Raft commit only | +| `RevokePrivilegeReq` | Coordinator | Coordinator | 10s | Raft commit only | +| `GetRolesReq` | Coordinator | Coordinator | 10s | Read served by the leader | +| `GetRolePrivilegesReq` | Coordinator | Coordinator | 10s | Read served by the leader | + + +`SetInstanceToMainReq` sends one `SwapMainUUID` per other instance, so its cost +grows with the number of data instances. The 60s budget comfortably covers five +instances. Beyond that, a follower can time out before the leader answers — no +fixed value bounds it. Run `SET INSTANCE ... TO MAIN` directly on the leader in +very large clusters. + +`ForceResetReq` triggers a reconciliation that retries under a 1s–60s backoff +for as long as the coordinator stays leader, so the leader-side work has no +upper bound at all. The 60s budget only keeps a genuinely wedged leader from +blocking the session — hitting it does **not** mean the reset failed, and +[`FORCE RESET CLUSTER +STATE`](/clustering/high-availability/ha-commands-reference#force-reset-cluster-state) +is safe to re-run. + + +The role and privilege RPCs are used by [coordinator +authentication](/clustering/high-availability/coordinator-authentication): +`GetRolesReq` in particular is sent on **every query of an SSO session**, because +coordinator privileges are re-derived from the leader's committed role set +rather than cached at login. + +{
System transaction timeouts
} + +MAIN-to-REPLICA system-delta RPCs are sent while committing a system +transaction, so they must not block indefinitely either. From Memgraph 3.13 each +carries a **10 second** timeout; a timeout marks the REPLICA as `BEHIND` and +defers to system recovery. + +| RPC message request | source | target | timeout | +|---------------------|--------|---------|---------| +| `UpdateAuthDataReq` | Main | Replica | 10s | +| `DropAuthDataReq` | Main | Replica | 10s | +| `FinalizeSystemTxReq` | Main | Replica | 10s | +| `CreateDatabaseReq` | Main | Replica | 10s | +| `DropDatabaseReq` | Main | Replica | 10s | +| `SuspendDatabaseReq` | Main | Replica | 10s | +| `ResumeDatabaseReq` | Main | Replica | 10s | +| `RenameDatabaseReq` | Main | Replica | 10s | +| `TenantProfileReq` | Main | Replica | 10s | +| `SetParameterReq` | Main | Replica | 10s | +| `UnsetParameterReq` | Main | Replica | 10s | +| `DeleteAllParametersReq`| Main | Replica | 10s | + ## Intra-cluster TLS By default, the communication between instances in a high-availability cluster diff --git a/pages/clustering/high-availability/querying-the-cluster-in-high-availability.mdx b/pages/clustering/high-availability/querying-the-cluster-in-high-availability.mdx index 74eaa3b51..9daf11db6 100644 --- a/pages/clustering/high-availability/querying-the-cluster-in-high-availability.mdx +++ b/pages/clustering/high-availability/querying-the-cluster-in-high-availability.mdx @@ -172,21 +172,51 @@ When using the **bolt+routing protocol**, provide credentials for users that exist on the data instances. The authentication flow works as follows: 1. The client authenticates and connects to a **coordinator**. -2. The coordinator returns a routing table - no authentication occurs here. +2. The coordinator returns a routing table. 3. The client connects to the appropriate **data instance** using the **same credentials**. 4. The data instance performs authentication and executes the query. -This preserves a clean separation: coordinators route traffic, while data -instances manage users. - -1. You may connect to a coordinator via plain Bolt **without authentication**. -2. When using Bolt+routing, you **must** provide credentials - authentication is +1. By default, you may connect to a coordinator via plain Bolt **without + authentication** — username and password are accepted as a passthrough and + ignored. +2. When using Bolt+routing, you **must** provide credentials — authentication is performed on the data instances. +### Authenticating against coordinators + +From Memgraph 3.13, coordinators are no longer unconditionally open. They can +enforce [single sign-on and coordinator +privileges](/clustering/high-availability/coordinator-authentication), which +changes the flow above in two ways: + +- **Step 1 becomes a real authentication.** Once SSO is configured on the + coordinators, basic auth is refused and the client must present a valid IdP + token for a scheme listed in `--auth-module-mappings`. +- **Step 2 requires a privilege.** Serving the routing table requires + `COORDINATOR_READ`. A session whose roles carry no coordinator privilege is + denied the routing table with `You don't have permission to read the routing + table on the coordinator!` — reported as a non-retryable client error so + drivers do not retry it. + + + +Because the driver reuses **one set of credentials for both legs** of a routing +connection, a `neo4j://` session works only when the **same role names exist on +both the coordinators and the data instances**, and the same SSO scheme is +configured on both. The privileges attached to those roles differ by design — +coordinators know only `COORDINATOR_READ` / `COORDINATOR_WRITE`, data instances +know only the data privilege set — but the names must match. + +See [Bolt+routing with +SSO](/clustering/high-availability/coordinator-authentication#boltrouting-with-sso) +for the full setup. + + + diff --git a/pages/database-management/authentication-and-authorization/auth-system-integrations.mdx b/pages/database-management/authentication-and-authorization/auth-system-integrations.mdx index db2a1eb4c..a91170d5c 100644 --- a/pages/database-management/authentication-and-authorization/auth-system-integrations.mdx +++ b/pages/database-management/authentication-and-authorization/auth-system-integrations.mdx @@ -154,6 +154,24 @@ This approach ensures that all roles are created before the external authentication module is activated, allowing users to log in seamlessly across all supported authentication methods. +If you cannot use `--init-file`, the equivalent manual procedure is to start the +data instance **without** `--auth-module-mappings`, log in, create the roles and +grant their privileges, then restart with the flag set. + + +From Memgraph 3.13, [high availability +coordinators](/clustering/high-availability/coordinator-authentication) also +support SSO, using these same modules, schemes and `MEMGRAPH_SSO_*` environment +variables. Coordinators **do not** need this stop-and-restart cycle: basic +authentication stays open until a role holding `COORDINATOR_WRITE` exists, so +you can start them with `--auth-module-mappings` from the beginning and create +the roles over Bolt on the running cluster. + +Note that a `neo4j://` routing connection authenticates against a coordinator +*and* a data instance with the same credentials, so the **same role names must +exist on both**. + + ## Auth module architecture ### Communication protocol diff --git a/pages/database-management/authentication-and-authorization/query-privileges.mdx b/pages/database-management/authentication-and-authorization/query-privileges.mdx index 7fb7ab614..034e9885b 100644 --- a/pages/database-management/authentication-and-authorization/query-privileges.mdx +++ b/pages/database-management/authentication-and-authorization/query-privileges.mdx @@ -156,7 +156,7 @@ Memgraph's privilege system controls access to various database operations throu | `REPLICATION` operations | `REPLICATION` | Various replication commands. | | `SHOW REPLICATION ROLE` | `REPLICATION` | `SHOW REPLICATION ROLE` | | `SHOW REPLICAS` | `REPLICATION` | `SHOW REPLICAS` | -| `SHOW REPLICATION LAG` | `COORDINATOR` | `SHOW REPLICATION LAG` | +| `SHOW REPLICATION LAG` | `COORDINATOR_READ` | Coordinators only — see [Coordinator operations](#coordinator-operations). | ## Multi-database operations @@ -191,10 +191,35 @@ Memgraph's privilege system controls access to various database operations throu ## Coordinator operations +These privileges apply **only on [high availability +coordinators](/clustering/high-availability/coordinator-authentication)**. They +are granted to roles that live in the coordinators' Raft-replicated cluster +state, not in the data-instance auth store, and are not part of the +data-instance privilege set — `GRANT ALL PRIVILEGES` on a data instance does not +grant them. + +`COORDINATOR_WRITE` is a superset of `COORDINATOR_READ`: a `COORDINATOR_WRITE` +grant satisfies a `COORDINATOR_READ` requirement. A session that connected to a +coordinator with basic auth carries both implicitly. + | Query Type | Required Privileges | Example | |------------|-------------------|---------| -| `COORDINATOR` operations | `COORDINATOR` | Various coordinator commands. | -| `SHOW COORDINATOR SETTINGS` | `COORDINATOR` | `SHOW COORDINATOR SETTINGS` | +| Routing table (Bolt `ROUTE` message) | `COORDINATOR_READ` | Any `neo4j://` connection. | +| `SHOW INSTANCE` / `SHOW INSTANCES` | `COORDINATOR_READ` | `SHOW INSTANCES` | +| `SHOW COORDINATOR SETTINGS` | `COORDINATOR_READ` | `SHOW COORDINATOR SETTINGS` | +| `SHOW REPLICATION LAG` | `COORDINATOR_READ` | `SHOW REPLICATION LAG` | +| `SHOW ROLES` / `SHOW PRIVILEGES FOR ROLE` | `COORDINATOR_READ` | `SHOW PRIVILEGES FOR ROLE dba` | +| `SHOW CONFIG`, `SHOW SETTING`, system info queries | `COORDINATOR_READ` | `SHOW CONFIG` | +| `SHOW CURRENT USER` / `SHOW CURRENT ROLE` | **None** | Self-service identity queries. | +| Cluster management operations | `COORDINATOR_WRITE` | `REGISTER INSTANCE`, `SET INSTANCE ... TO MAIN`, `ADD COORDINATOR`, `YIELD LEADERSHIP`, `FORCE RESET CLUSTER STATE`, … | +| `SET COORDINATOR SETTING` / `SET SETTING` | `COORDINATOR_WRITE` | `SET COORDINATOR SETTING 'sync_failover_only' TO 'true'` | +| `CREATE ROLE` / `DROP ROLE` / `GRANT` / `REVOKE` | `COORDINATOR_WRITE` | `GRANT COORDINATOR_READ TO analyst` | + + +**Breaking change in Memgraph 3.13:** the `COORDINATOR` privilege was removed +and replaced by `COORDINATOR_READ` and `COORDINATOR_WRITE`. It never gated any +operation and is no longer accepted in `GRANT` / `DENY` / `REVOKE` statements. + ## Schema information diff --git a/pages/database-management/authentication-and-authorization/role-based-access-control.mdx b/pages/database-management/authentication-and-authorization/role-based-access-control.mdx index cc2259042..d5ddaf21a 100644 --- a/pages/database-management/authentication-and-authorization/role-based-access-control.mdx +++ b/pages/database-management/authentication-and-authorization/role-based-access-control.mdx @@ -243,7 +243,6 @@ of the following commands: | Privilege to change [storage mode](/fundamentals/storage-memory-usage#storage-modes). | `STORAGE_MODE` | | Privilege to manage [multi-tenant databases](/database-management/multi-tenancy). | `MULTI_DATABASE_EDIT` | | Privilege to use a database within the multi-tenant architecture. | `MULTI_DATABASE_USE` | -| Privilege to configure [high-availability](/clustering/high-availability) coordinators. | `COORDINATOR` | | Privilege to [impersonate other users](/database-management/authentication-and-authorization/impersonate-user). | `IMPERSONATE_USER` | | Privilege to use [parallel execution](/querying/parallel-execution). | `PARALLEL_EXECUTION` | | Privilege to set limits and monitor resource usage per user (via [user profiles](/database-management/authentication-and-authorization/user-profiles)) or per database (via [tenant profiles](/database-management/tenant-profiles)). | `PROFILE_RESTRICTION` | @@ -257,6 +256,22 @@ of the following commands: For a comprehensive reference of which privileges are required for specific queries and operations, see the [Query privileges reference](/database-management/authentication-and-authorization/query-privileges) documentation. + +**Breaking change in Memgraph 3.13:** the `COORDINATOR` privilege was removed — +it never gated any operation. It is no longer accepted in `GRANT` / `DENY` / +`REVOKE` statements and is no longer reported by `SHOW PRIVILEGES`. A stale +`COORDINATOR` grant in an existing deployment is never reported and never +checked, so no migration is needed. + +Access to [high availability](/clustering/high-availability) coordinators is now +controlled by the `COORDINATOR_READ` and `COORDINATOR_WRITE` privileges. These +are **coordinator-only**: they are granted to roles that live on the +coordinators themselves, they are not part of the data-instance privilege set, +and `GRANT ALL PRIVILEGES` on a data instance does not grant them. See +[coordinator +authentication](/clustering/high-availability/coordinator-authentication). + + ### First user privileges When you create the first user in Memgraph, that user automatically becomes a diff --git a/pages/database-management/enabling-memgraph-enterprise.mdx b/pages/database-management/enabling-memgraph-enterprise.mdx index 0c33f655a..5dc2ac744 100644 --- a/pages/database-management/enabling-memgraph-enterprise.mdx +++ b/pages/database-management/enabling-memgraph-enterprise.mdx @@ -245,6 +245,12 @@ ideal solution for those who need a worry-free, highly available system with failover ensures your system remains operational with minimal downtime and manual intervention. +The cluster's control plane can also be secured: [coordinator +authentication](/clustering/high-availability/coordinator-authentication) lets +coordinators authenticate OIDC, SAML and Kerberos connections against your +identity provider and enforce the `COORDINATOR_READ` and `COORDINATOR_WRITE` +privileges on Raft-replicated roles. + ### Multi-tenancy [Multi-tenant support](/database-management/multi-tenancy) enables you to manage diff --git a/pages/release-notes.mdx b/pages/release-notes.mdx index e01e0d244..41b538b80 100644 --- a/pages/release-notes.mdx +++ b/pages/release-notes.mdx @@ -58,6 +58,19 @@ guide. you need the previous Python behavior, or update consumers for the new structured / ISO-8601 JSON forms. [#4443](https://github.com/memgraph/memgraph/pull/4443) +- The `COORDINATOR` privilege was removed — it never gated any operation. It is + no longer accepted in `GRANT` / `DENY` / `REVOKE` statements and is no longer + reported by `SHOW PRIVILEGES`. A stale grant in an existing deployment is + never reported and never checked, so no migration is required. Coordinator + access is now controlled by the new `COORDINATOR_READ` and `COORDINATOR_WRITE` + privileges. + [#4399](https://github.com/memgraph/memgraph/pull/4399) +- Connecting to a coordinator with a username and password is no longer always + accepted. Once `--auth-module-mappings` is set, the enterprise license is + valid, **and** a role holding `COORDINATOR_WRITE` exists, basic authentication + is denied in favor of SSO. Until all three hold, the previous passthrough + behavior is unchanged. + [#4399](https://github.com/memgraph/memgraph/pull/4399) {

🐞 Bug fixes

} @@ -128,6 +141,15 @@ guide. statements). Under index-heavy workloads this removes GC-correlated latency spikes on index and constraint creation. [#4468](https://github.com/memgraph/memgraph/pull/4468) +- Cluster management queries forwarded from a follower coordinator to the leader + now have [explicit RPC + timeouts](/clustering/high-availability/how-high-availability-works#rpc-timeouts), + so a Bolt session can no longer block indefinitely against a leader that is + reachable but stuck. The same applies to MAIN-to-REPLICA system transaction + messages (auth, multi-tenancy, parameters), which now time out after 10s and + defer to system recovery. The unused `EnableWritingOnMainRpc` and its + Prometheus counters were removed. + [#4399](https://github.com/memgraph/memgraph/pull/4399) {

✨ New features

} @@ -160,6 +182,18 @@ guide. - `CREATE RANGE INDEX FOR ... ON ...` now works for nodes and relationships and creates Memgraph’s usual property index. [#4486](https://github.com/memgraph/memgraph/pull/4486) +- [SSO authentication on high availability + coordinators](/clustering/high-availability/coordinator-authentication) + (Enterprise). Coordinators authenticate OIDC, SAML and Kerberos connections + against a Raft-replicated set of roles and enforce two privileges, + `COORDINATOR_READ` and `COORDINATOR_WRITE`. Manage them with `CREATE ROLE`, + `DROP ROLE`, `SHOW ROLES`, `GRANT` / `REVOKE COORDINATOR_READ | + COORDINATOR_WRITE | ALL PRIVILEGES`, and `SHOW PRIVILEGES FOR ROLE` on any + coordinator — follower queries are forwarded to the leader. Basic + authentication stays open until a `COORDINATOR_WRITE` role exists, so no + coordinator restart is needed to roll SSO out, and it reopens if the license + becomes invalid so a coordinator can never lock every session out. + [#4399](https://github.com/memgraph/memgraph/pull/4399) ### Lab v3.13.0 - September 9th, 2026 From 9761fb2a92c955d7862e360aa865e3378ca0af0b Mon Sep 17 00:00:00 2001 From: as51340 Date: Tue, 4 Aug 2026 13:09:29 +0200 Subject: [PATCH 2/4] feat: Remove --init-file comment --- .../coordinator-authentication.mdx | 8 -------- pages/release-notes.mdx | 14 +++++++++----- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/pages/clustering/high-availability/coordinator-authentication.mdx b/pages/clustering/high-availability/coordinator-authentication.mdx index 9b0907c8f..5bbe2cd65 100644 --- a/pages/clustering/high-availability/coordinator-authentication.mdx +++ b/pages/clustering/high-availability/coordinator-authentication.mdx @@ -506,14 +506,6 @@ SSO logins now find their roles and succeed. - -In containerized deployments you can collapse this into a single start by -creating the roles with -[`--init-file`](/database-management/authentication-and-authorization/auth-system-integrations#docker-deployment-note), -which runs a Cypher script before the database begins serving connections. That -avoids the stop-and-restart cycle entirely. - - Do the coordinator side and the data-instance side with the **same role names**, or [Bolt+routing](#boltrouting-with-sso) connections will fail even though diff --git a/pages/release-notes.mdx b/pages/release-notes.mdx index 41b538b80..3d888a450 100644 --- a/pages/release-notes.mdx +++ b/pages/release-notes.mdx @@ -59,11 +59,15 @@ guide. structured / ISO-8601 JSON forms. [#4443](https://github.com/memgraph/memgraph/pull/4443) - The `COORDINATOR` privilege was removed — it never gated any operation. It is - no longer accepted in `GRANT` / `DENY` / `REVOKE` statements and is no longer - reported by `SHOW PRIVILEGES`. A stale grant in an existing deployment is - never reported and never checked, so no migration is required. Coordinator - access is now controlled by the new `COORDINATOR_READ` and `COORDINATOR_WRITE` - privileges. + no longer accepted in `GRANT` / `DENY` / `REVOKE` statements, which now fail + with a syntax error, and it is no longer reported by `SHOW PRIVILEGES` or + included in `GRANT ALL PRIVILEGES`. Remove any `GRANT COORDINATOR` / + `DENY COORDINATOR` / `REVOKE COORDINATOR` statements from provisioning scripts + and update tooling that compares against the full privilege list. No data + migration is required: a stale grant in an existing deployment is never + reported and never checked. The new `COORDINATOR_READ` and `COORDINATOR_WRITE` + privileges are not a replacement — they are coordinator-only and are rejected + on data instances. [#4399](https://github.com/memgraph/memgraph/pull/4399) - Connecting to a coordinator with a username and password is no longer always accepted. Once `--auth-module-mappings` is set, the enterprise license is From 0d96d9ac8752fcf0a990186f4671a4d07d45dfdd Mon Sep 17 00:00:00 2001 From: as51340 Date: Tue, 4 Aug 2026 13:18:20 +0200 Subject: [PATCH 3/4] fix: Notes if SSO on data instances is already set-up --- .../high-availability/coordinator-authentication.mdx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pages/clustering/high-availability/coordinator-authentication.mdx b/pages/clustering/high-availability/coordinator-authentication.mdx index 5bbe2cd65..836d8a4c7 100644 --- a/pages/clustering/high-availability/coordinator-authentication.mdx +++ b/pages/clustering/high-availability/coordinator-authentication.mdx @@ -477,6 +477,13 @@ SHOW PRIVILEGES FOR ROLE dba; ### On data instances — restart required + +This only concerns data instances where SSO is **not** set up yet. If your data +instances already authenticate through SSO, nothing changes for them — enabling +SSO on the coordinators does not require any modification on the data-instance +side, beyond using the [same role names](#boltrouting-with-sso) on both sides. + + Data instances have no equivalent escape hatch. Their SSO path validates the IdP's roles against the auth store, and a role that does not exist means the login fails. If you enable the module before the roles exist, SSO users cannot From cab005af4927171c8f3c9d9ae291cb1a8a3b584a Mon Sep 17 00:00:00 2001 From: as51340 Date: Tue, 4 Aug 2026 13:20:47 +0200 Subject: [PATCH 4/4] fix: State definitively that --init-file is unsupported in HA mode --- .../auth-system-integrations.mdx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/pages/database-management/authentication-and-authorization/auth-system-integrations.mdx b/pages/database-management/authentication-and-authorization/auth-system-integrations.mdx index a91170d5c..252430ec0 100644 --- a/pages/database-management/authentication-and-authorization/auth-system-integrations.mdx +++ b/pages/database-management/authentication-and-authorization/auth-system-integrations.mdx @@ -154,9 +154,14 @@ This approach ensures that all roles are created before the external authentication module is activated, allowing users to log in seamlessly across all supported authentication methods. -If you cannot use `--init-file`, the equivalent manual procedure is to start the -data instance **without** `--auth-module-mappings`, log in, create the roles and -grant their privileges, then restart with the flag set. +This workflow only applies to standalone Memgraph instances. On [data instances +running in HA +mode](/clustering/high-availability/how-high-availability-works#data-instance-implementation) +(i.e. when `--management-port` is set), `--init-file` is **not supported** and +the instance will fail to start if the flag is provided. There you must use the +manual procedure instead: start the data instance **without** +`--auth-module-mappings`, log in, create the roles and grant their privileges, +then restart with the flag set. From Memgraph 3.13, [high availability