Skip to main content

Authentication

This guide covers authentication options for Fluree Server and Fluree Cloud.


Overview​

Fluree supports several authentication methods depending on your deployment:

DeploymentAuthentication Method
Fluree Server (open)No authentication required (default)
Fluree Server (closed)Root identities via closedMode config
Fluree CloudAPI keys via Authorization header
Policy-basedIdentity headers for per-request policies

Fluree Server Authentication​

Open Mode (Default)​

By default, Fluree Server runs in open mode with no authentication. All endpoints accept unauthenticated requests:


curl -X POST "http://localhost:8090/fluree/query" \
-H "Content-Type: application/json" \
-d '{"from": "my-ledger", "select": {"?s": ["*"]}, "where": {"@id": "?s"}}'

This is suitable for development and trusted internal networks.

Closed Mode​

For production environments, enable closedMode to require authentication:


# fluree-config.edn
{:fluree.server/closed-mode {:root-identities ["did:fluree:admin123"]}}

Or via environment variable:


export FLUREE_SERVER_CLOSED_MODE_ROOT_IDENTITIES='["did:fluree:admin123"]'

In closed mode, requests must include a valid identity. See Server Configuration for details.


Fluree Cloud Authentication​

Fluree Cloud requires API key authentication for all requests.

Generating an API Key​

  1. Navigate to your dataset in Fluree Cloud
  2. Go to Settings > API Keys
  3. (Optional) Name your API key
  4. (Optional) Assign roles to limit permissions
  5. Click Generate Key
  6. Copy the key — it's only shown once

NOTE: If you don't assign a role, the API key has full read-write access.

Using API Keys​

Include the API key in the Authorization header:


curl -X POST "https://data.flur.ee/fluree/query" \
-H "Content-Type: application/json" \
-H "Authorization: YOUR_API_KEY" \
-d '{
"from": "fluree-jld/387028092977716",
"select": {"?s": ["*"]},
"where": {"@id": "?s", "@type": "ex:Person"}
}'

JavaScript Example​


const API_KEY = 'your-api-key-here';
const DATASET_ID = 'fluree-jld/387028092977716';
const response = await fetch('https://data.flur.ee/fluree/query', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': API_KEY
},
body: JSON.stringify({
from: DATASET_ID,
select: { '?s': ['*'] },
where: { '@id': '?s', '@type': 'ex:Person' }
})
});
const data = await response.json();
console.log(data);

Python Example​


import requests
API_KEY = 'your-api-key-here'
DATASET_ID = 'fluree-jld/387028092977716'
response = requests.post(
'https://data.flur.ee/fluree/query',
headers={
'Content-Type': 'application/json',
'Authorization': API_KEY
},
json={
'from': DATASET_ID,
'select': {'?s': ['*']},
'where': {'@id': '?s', '@type': 'ex:Person'}
}
)
print(response.json())


Identity Headers​

For policy-based access control, use identity headers to specify who is making the request. This works with both self-hosted and cloud deployments.

Available Headers​

HeaderDescription
fluree-identityUser identity (DID or IRI) for policy evaluation
fluree-policyInline policy document (JSON string)
fluree-policy-identityIdentity for policy lookup
fluree-policy-classPolicy class to apply
fluree-policy-valuesValues to bind in policy evaluation (JSON string)

Example: Query as Specific User​


curl -X POST "http://localhost:8090/fluree/query" \
-H "Content-Type: application/json" \
-H "fluree-identity: ex:alice" \
-d '{
"from": "my-ledger",
"select": {"?s": ["*"]},
"where": {"@id": "?s"}
}'

The query results will be filtered based on any policies defined for ex:alice.

Example: Apply Inline Policy​


curl -X POST "http://localhost:8090/fluree/query" \
-H "Content-Type: application/json" \
-H 'fluree-policy: {"@id": "ex:readOnly", "f:targetClass": {"@id": "ex:Person"}, "f:allow": [{"@id": "f:view"}]}' \
-d '{
"from": "my-ledger",
"select": {"?s": ["*"]},
"where": {"@id": "?s"}
}'

For more on policies, see Policy Syntax and Data Access Control.


Verifiable Credentials (JWS)​

Fluree supports cryptographically signed requests using JSON Web Signatures (JWS). This provides:

  • Authentication — Verify who is making the request
  • Integrity — Ensure the request hasn't been tampered with
  • Non-repudiation — Cryptographic proof of the request origin

JWS Request Format​

Set the Content-Type header to application/jwt and send the signed JWS as the request body:


curl -X POST "http://localhost:8090/fluree/create" \
-H "Content-Type: application/jwt" \
-d 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19...'

The JWS payload contains the JSON query or transaction, signed with an Ed25519 private key.

DID (Decentralized Identifier)​

Fluree uses did:key format for identities. A DID is derived from your public key:


did:key:z6MkmbNqfM3ANYZnzDp9YDfa62pHggKosBkCyVdgQtgEKkGQ

Creating Signed Requests​

Use the Fluree crypto library to sign requests:


import { createJws } from '@fluree/crypto';
const privateKey = 'your-ed25519-private-key-hex';
const query = {
from: "my-ledger",
select: { "?s": ["*"] },
where: { "@id": "?s" }
};
const jws = createJws(JSON.stringify(query), privateKey, { includePubkey: true });

Credential-Wrapped Operations​

When using the Fluree library directly, use the credential functions:

Signed Transaction:


import { credentialUpdate } from '@fluree/db';
const signedTx = await credential.generate({
ledger: "my-ledger",
insert: { "@id": "ex:newEntity", "ex:name": "Test" }
}, privateKey);
await fluree.credentialUpdate(conn, signedTx);

Signed Query:


import { credentialQuery } from '@fluree/db';
const signedQuery = await credential.generate({
from: "my-ledger",
select: { "?s": ["*"] },
where: { "@id": "?s" }
}, privateKey);
const results = await fluree.credentialQuery(db, signedQuery);

Signed History Query:


import { credentialHistory } from '@fluree/db';
const signedHistory = await credential.generate({
history: "ex:entity",
t: { from: 1 }
}, privateKey);
const history = await fluree.credentialHistory(conn, "my-ledger", signedHistory);


Closed Mode (Server)​

Closed mode requires all requests to be cryptographically signed. This is essential for production deployments where you need strict access control.

Enabling Closed Mode​

Configure closed mode in your server configuration:


;; fluree-config.edn
{:fluree.server/closed-mode
{:root-identities ["did:key:z6MkmbNqfM3ANYZnzDp9YDfa62pHggKosBkCyVdgQtgEKkGQ"]}}

Or via environment variable:


export FLUREE_SERVER_CLOSED_MODE_ROOT_IDENTITIES='["did:key:z6MkmbNqfM3ANYZnzDp9YDfa62pHggKosBkCyVdgQtgEKkGQ"]'

Root vs Non-Root Identities​

ActionRoot IdentityNon-Root Identity
Create ledger✅ Allowed❌ Rejected (403)
Drop ledger✅ Allowed❌ Rejected (403)
Transact✅ AllowedSubject to policies
Query✅ AllowedSubject to policies
History✅ AllowedSubject to policies

Root identities can perform administrative operations. Non-root identities can only access data permitted by policies.

Closed Mode Request Flow​

  1. Request arrives — Server checks for JWS signature
  2. Missing signature → 400 "Missing credential"
  3. Signature verified → Extract DID from public key
  4. DID checked → Is it a root identity?
  5. Root identity → Full access to operation
  6. Non-root identity → Apply policies, filter results

Example: Closed Mode Requests​

Create Ledger (Root Only):


# Sign the request with a root identity's private key
JWS=$(fluree-crypto sign '{
"ledger": "my-ledger",
"insert": {"@id": "ex:initial", "ex:name": "Initial Data"}
}' $ROOT_PRIVATE_KEY)
curl -X POST "http://localhost:8090/fluree/create" \
-H "Content-Type: application/jwt" \
-d "$JWS"

Query (Any Signed Request):


JWS=$(fluree-crypto sign '{
"from": "my-ledger",
"select": {"?s": ["*"]},
"where": {"@id": "?s"}
}' $USER_PRIVATE_KEY)
curl -X POST "http://localhost:8090/fluree/query" \
-H "Content-Type: application/jwt" \
-d "$JWS"

Setting Up Policies for Non-Root Users​

For non-root identities to access data, define policies in your ledger:


{
"insert": [
{
"@id": "ex:userPolicy",
"@type": ["f:AccessPolicy", "ex:DefaultUserPolicy"],
"f:action": [{"@id": "f:view"}, {"@id": "f:modify"}],
"f:query": {
"@type": "@json",
"@value": {
"where": [["filter", "(= ?$this ?$identity)"]]
}
}
},
{
"@id": "did:key:z6MkiKJFxJJd9QuqKgzBR2kiSybE9V2517sFd2kTS7kQe9mg",
"f:policyClass": {"@id": "ex:DefaultUserPolicy"},
"@type": "ex:User",
"ex:name": "Alice"
}
]
}

This policy allows users to view and modify only their own data (where ?$this equals ?$identity).

Security Considerations​

  1. Protect private keys — Never expose private keys; use secure key management
  2. Use unique identities — Each user/service should have its own key pair
  3. Audit root access — Limit and monitor root identity usage
  4. Rotate keys — Periodically rotate key pairs for compromised key recovery

Resource Control​

These headers help control and monitor request resource usage:

HeaderDescription
fluree-max-fuelMaximum computational units allowed
fluree-track-fuelInclude fuel consumption in response
fluree-track-metaInclude metadata in response
fluree-track-policyInclude policy evaluation details

Example: Track Fuel Usage​


curl -X POST "http://localhost:8090/fluree/query" \
-H "Content-Type: application/json" \
-H "fluree-max-fuel: 10000" \
-H "fluree-track-fuel: true" \
-d '{
"from": "my-ledger",
"select": {"?s": ["*"]},
"where": {"@id": "?s"}
}'

The response will include fuel consumption in the x-fdb-fuel header.


Best Practices​

  1. Never commit API keys — Use environment variables or secret management
  2. Use least privilege — Assign roles to API keys that limit access
  3. Rotate keys regularly — Generate new keys and revoke old ones periodically
  4. Use HTTPS — Always use encrypted connections in production
  5. Monitor usage — Track fuel consumption to detect anomalies