FlureeLabs

Authentication

Fluree supports multiple authentication mechanisms to cover different deployment scenarios — from standalone servers with no external identity provider to managed platforms using OIDC.

This document describes the authentication model, the supported modes, the bearer token claim set, and the access boundary between replication and query operations.

Identity vs transport

Identity (who)

Fluree policy enforcement is based on an identity, ideally a DID:

  • Preferred: did:key:... — portable across environments, no central identity server required
  • Also possible: other DIDs or IRIs mapped into Fluree policy (e.g. ex:alice)

Policies are stored as RDF triples in the ledger and evaluated at query/transaction time against the requesting identity. See Policy model for details.

Transport (how requests authenticate)

Two "on-the-wire" mechanisms carry the identity:

MechanismFormatWhen to use
Signed requestsJWS/VC envelope containing the DIDProof-of-possession; trustless environments
Bearer tokensAuthorization: Bearer <JWT>Session-based; OIDC/OAuth2 flows

Bearer tokens are a UX and deployment convenience — they do not replace the identity model. The server extracts the identity from the token claims and enforces the same dataset policies as signed requests.

Three supported auth modes

Mode 1 — Decentralized: did:key signed requests (no IdP)

  • The client holds an Ed25519 keypair and derives a did:key:...
  • Requests are signed using JWS or Verifiable Credential format
  • The server verifies the signature and uses the DID as the principal
  • Dataset policies decide allow/deny

This preserves Fluree's core value: no central identity server required.

See Signed requests (JWS/VC) for the wire format.

Mode 2 — Standalone server with offline-minted tokens

Designed for: "stand up a server somewhere" (local dev, single-node EC2, etc.).

  • An admin generates an Ed25519 keypair with fluree token keygen
  • The admin mints a scoped Bearer token with fluree token create
  • The admin provides the token to CLI users or stores it in a secret manager
  • The server validates the token's embedded JWK signature and enforces scopes + policy

The policy identity remains DID-based (fluree.identity claim), so authorization stays dataset/policy driven even though the transport is a Bearer token.

See CLI token command for minting instructions.

Mode 3 — OIDC/OAuth2 with an external identity provider

Designed for: managed platforms (e.g., any application using an OIDC provider).

  • The IdP authenticates the user (device flow, PKCE, etc.)
  • The application knows the user's Fluree dataset entitlements
  • The application issues (or exchanges for) a Fluree-scoped token carrying:
    • identity (fluree.identity — ideally a DID)
    • ledger read/write scopes
    • optional policy class
  • The server verifies the token against the provider's JWKS endpoint

This preserves separation of concerns:

  • IdP: authentication (who logged in)
  • Application: authorization (what they can access in Fluree)

The server must be configured with --jwks-issuer to trust OIDC tokens. See Configuration — OIDC.

Bearer token claim set

All Fluree Bearer tokens (Mode 2 and Mode 3) share the same claim set. The server extracts identity and scopes from these claims regardless of how the token was signed.

Standard JWT claims

ClaimRequiredDescription
issYesIssuer — did:key:... for Ed25519 tokens, URL for OIDC tokens
subNoSubject — human-readable identity of the token holder
audNoAudience — target service (e.g. server URL)
expYesExpiration time (Unix timestamp)
iatYesIssued-at time (Unix timestamp)

Fluree-specific claims

ClaimTypeDescription
fluree.identityString (IRI/DID)Identity for policy enforcement — takes precedence over sub
fluree.policy.classString (IRI)Optional policy class for identity-based policy lookup

Scope claims

Scopes control which endpoints and ledgers a token can access.

Query scopes (fluree.ledger.*)

ClaimTypeDescription
fluree.ledger.read.allBooleanRead access to all ledgers via data API
fluree.ledger.read.ledgersArray of stringsRead access to specific ledgers
fluree.ledger.write.allBooleanWrite access to all ledgers via data API
fluree.ledger.write.ledgersArray of stringsWrite access to specific ledgers

Replication scopes (fluree.storage.*)

ClaimTypeDescription
fluree.storage.allBooleanStorage/replication access to all ledgers
fluree.storage.ledgersArray of stringsStorage/replication access to specific ledgers

Back-compat: fluree.storage.* claims also imply data API read access for the same ledgers.

Populating fluree.storage.ledgers (multi-tenant hint)

If you run an IdP or a request-router that exchanges IdP tokens for Fluree-scoped tokens, prefer populating fluree.storage.ledgers rather than granting fluree.storage.all.

Recommended conventions for mapping IdP group/role claims to ledger scopes:

  • Treat group values like fluree:storage:<ledger-id> (example: fluree:storage:books:main) as permission to replicate that ledger.
  • Optionally support wildcards at the router boundary (example: fluree:storage:books:* expands to the set of ledgers your router knows about under books:).
  • Reserve fluree.storage.all=true for admin/service accounts.

Event scopes (fluree.events.*)

ClaimTypeDescription
fluree.events.allBooleanSSE event stream for all ledgers
fluree.events.ledgersArray of stringsSSE event stream for specific ledgers

Example token payload

{
  "iss": "https://solo.example.com",
  "sub": "alice@example.com",
  "aud": "https://fluree.example.com",
  "exp": 1700000000,
  "iat": 1699996400,
  "fluree.identity": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
  "fluree.ledger.read.all": true,
  "fluree.ledger.write.ledgers": ["mydb:main", "mydb:staging"]
}

Token verification paths

The server supports two verification paths, selected automatically based on the JWT header:

JWT headerPathAlgorithmTrust model
Contains jwk (embedded key)Ed25519 / did:keyEdDSAIssuer trust checked against --events-auth-trusted-issuer (or admin/storage equivalents)
Contains kid (key ID)OIDC / JWKSRS256Issuer must match a --jwks-issuer; key fetched from JWKS endpoint

This dual-path dispatch is transparent to callers — the same Authorization: Bearer <token> header works for both paths. The server applies the same scope and identity enforcement regardless of which path verified the signature.

Replication vs query access boundary

Fluree draws a hard boundary between replication-scoped and query-scoped access.

Replication access (fluree.storage.*)

Replication operations — nameservice sync, storage proxy reads, and CLI fetch/pull/push — require root-level fluree.storage.* claims. These operations transfer raw commit data and index blocks; they bypass dataset policy because the data must be bit-identical to what the transaction server wrote.

Replication tokens are intended for operator and service-account use (e.g. a peer server's storage-proxy token, or an admin's CLI pull/push workflow). They should never be issued to end users.

Query access (fluree.ledger.read/write.*)

Query operations — /v1/fluree/query/{ledger...}, /v1/fluree/insert/{ledger...}, connection-scoped SPARQL, etc. — use fluree.ledger.read/write.* claims. These go through the full query engine and dataset policy enforcement. The server never exposes raw storage bytes through query endpoints.

Query tokens are appropriate for end users and application service accounts. Combined with a fluree.identity claim and dataset policies, the server enforces fine-grained row- and property-level access control.

CLI consequence: track vs pull

CommandAccess typeRequired scopeWhat happens
fluree pullReplicationfluree.storage.*Downloads raw commits and indexes into local storage
fluree trackQueryfluree.ledger.read/write.*Registers a remote ledger; queries forwarded to server

If a user holds only query-scoped tokens, they cannot clone or pull a ledger. They can only track it and issue queries/transactions against the remote.

Identity precedence

When multiple identity signals are present, the server uses this precedence (highest first):

  1. Signed request DID — proof-of-possession from JWS/VC signature
  2. Bearer token fluree.identity — identity claim in the token
  3. Client-provided headers/body — only honored when the server is in unauthenticated mode

When auth is present, the server forces opts.identity (and optional policy class) from the token, ignoring any client-provided identity in headers or request bodies. This prevents identity spoofing.

Endpoint coverage

All Bearer-token-authenticated endpoints support both Ed25519 and OIDC verification paths:

Endpoint groupExtractorScopes checked
Data API (query/update/info/exists)MaybeDataBearerfluree.ledger.read/write.*
Admin (create/drop)require_admin_tokenIssuer trust
Events (SSE)MaybeBearerfluree.events.*
Storage proxyStorageProxyBearerfluree.storage.*
Nameservice refsStorageProxyBearerfluree.storage.*

MCP endpoints currently use the Ed25519 path only.

Security notes

  • Tokens are validated server-side on every request; client-side validation is never trusted
  • Out-of-scope ledgers return 404 (not 403) to avoid existence leaks
  • fluree.storage.* tokens grant raw data access — issue only to trusted operators
  • Connection-scoped SPARQL (FROM/FROM NAMED) requires all referenced ledgers to be within the token's read scope

See also